using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Audio;
using Bunbyx.exception;
using System;
using System.Collections.Generic;

namespace Bunbyx.game
{
    // --------------------------------------------------------------------
    //  The Game
    // --------------------------------------------------------------------

    /// <summary>
    /// This is the game
    /// </summary>
    public class BunbyxGame : Game
    {
        // --------------------------------------------------------------------
        //  Constants
        // --------------------------------------------------------------------

        public const string CONTENT_ROOT_DIRECTORY = "Content";
        private const int RESOLUTION_WIDTH = 1024;
        private const int RESOLUTION_HEIGHT = 640;

        // -------------------------------------------------------------------
        //  Enumeration
        // --------------------------------------------------------------------

        private enum GameState
        {
            Playing,
            Paused,
            BetweenLevels,
            InMenu,
            Stopped
        }

        // --------------------------------------------------------------------
        //  Private attributes
        // --------------------------------------------------------------------

        // XNA stuff
        private GraphicsDeviceManager graphics;
        private SpriteBatch spriteBatch;

        private SpriteFont font;

        // Camera
        private Camera camera;

        // HUD
        private Hud hud;

        // Player controls
        private KeyboardListener keyboard;

        // Screens
        private Screen currentScreen;

        // Sounds
        private SoundPlayer soundPlayer;

        // Level management
        private Level level;
        private string[] levels = {
            //"04 - Alice",
            //"00 - Multipart",
            "00 - Test",
            "01 - LVL1",
            "02 - LVL2",
            "03 - LVL3",
            "04 - LVL4",
            "Alice - Le terrier",
            "Alice - La table du chapelier"
        };
        bool doingAlice = false;
        private string[] levelsToDoAlice = {
            "Alice - Le terrier",
            "Alice - La table du chapelier"
        };
        private int ctLevel = 0;

        private Dictionary<string, long> bestScores;

        // State
        private GameState state;
        // --------------------------------------------------------------------
        //  Constructor
        // --------------------------------------------------------------------

        public BunbyxGame()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = CONTENT_ROOT_DIRECTORY;
            bestScores = new Dictionary<string, long>();

            foreach (String level in levels)
            {
                bestScores.Add(level, 0);
            }
        }

        // --------------------------------------------------------------------
        //  XNA methods
        // --------------------------------------------------------------------

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // Change the resolution
            graphics.PreferredBackBufferWidth = RESOLUTION_WIDTH;
            graphics.PreferredBackBufferHeight = RESOLUTION_HEIGHT;
            graphics.ApplyChanges();

            state = GameState.InMenu;

            // Create camera
            camera = new Camera(graphics.GraphicsDevice.Viewport);

            // Create HUD
            hud = new Hud(camera);

            // Create sound player
            soundPlayer = new SoundPlayer();

            // Create screen
            currentScreen = new WelcomeScreen(StartGame, ExitGame, levels, soundPlayer, setDoingAliceState, bestScores);

            // Create keyboard
            keyboard = new KeyboardListener(PauseGame);

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            font = this.Content.Load<SpriteFont>("font");

            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // Load current screen
            currentScreen.LoadContent(Content);

            // Load Sounds
            soundPlayer.LoadContent(Content);

            // Load HUD
            hud.LoadContent(Content);
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // Nothing to unload
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Update screens
            if (currentScreen != null)
            {
                currentScreen.Update(gameTime);
            }

            if (state == GameState.Playing)
            {
                // Update level
                level.Update(graphics, spriteBatch, gameTime);

                // Update HUD
                hud.Update(gameTime);
            }
            else if (state == GameState.InMenu)
            {
                camera.Reset();
            }

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            // Clear screen
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // Begin drawing
            spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, camera.View);

            // Draw level
            if (level != null)
            {
                level.Draw(spriteBatch);
            }

            // Draw HUD
            hud.Draw(spriteBatch);
            //spriteBatch.DrawString(font, (GC.GetTotalMemory(false) / 1000).ToString(), new Vector2(camera.Position.X, camera.Position.Y), Color.White);

            // Draw current screen
            if (currentScreen != null)
            {
                currentScreen.Draw(spriteBatch);
            }

            // End of drawing
            spriteBatch.End();

            base.Draw(gameTime);
        }

        // --------------------------------------------------------------------
        //  Public methods
        // --------------------------------------------------------------------

        public void MainMenu()
        {
            state = GameState.InMenu;
            level = null;
            soundPlayer.StopMusic();
            changeScreen(new WelcomeScreen(StartGame, ExitGame, levels, soundPlayer, setDoingAliceState, bestScores));
        }

        public void StartGame(string level)
        {
            loadLevel(level, true);
            removeScreen();
            state = GameState.Playing;
        }

        public void PauseGame()
        {
            if (state == GameState.Playing)
            {
                changeScreen(new PauseScreen(camera, PauseGame, RestartLevel, MainMenu));
                state = GameState.Paused;
            }
            else
            {
                removeScreen();
                state = GameState.Playing;
            }
        }

        public void RestartLevel()
        {
            level.RestartLevel();
        }

        public void LevelEnded()
        {
            state = GameState.BetweenLevels;
            ScoreScreen scoreScreen = new ScoreScreen(camera, hud, NextLevel);
            long score = scoreScreen.GetTotalScore();

            if (score > bestScores[level.Name])
            {
                bestScores[level.Name] = score;
                scoreScreen.IsBestScore();
            }

            changeScreen(scoreScreen);
        }

        public void NextLevel()
        {
            if (doingAlice && ctLevel < levelsToDoAlice.Length - 1)
            {
                ctLevel++;
                StartGame(levelsToDoAlice[ctLevel]);
            }
            else
            {
                ctLevel = 0;
                state = GameState.InMenu;
                changeScreen(new WelcomeScreen(StartGame, ExitGame, levels, soundPlayer, setDoingAliceState, bestScores));
                camera.Reset();
            }
        }

        public void ItemPickedUp(int value, Sprite.SpriteType type)
        {
            hud.Score += value;

            switch (type)
            {
                case Sprite.SpriteType.Salad:
                    hud.SaladCount++;
                    break;

                case Sprite.SpriteType.Carrot:
                    hud.CarrotCount++;
                    break;

                case Sprite.SpriteType.IdolGold:
                    hud.IdolGoldCount++;
                    break;
            }
        }

        public void setDoingAliceState(bool state)
        {
            doingAlice = state;
        }

        // --------------------------------------------------------------------
        //  Private methods
        // --------------------------------------------------------------------

        private void loadLevel(string levelName, bool warmLoad)
        {
            level = new Level(levelName, camera, keyboard, soundPlayer, ItemPickedUp, LevelEnded);

            removeScreen();
            state = GameState.Playing;

            if (warmLoad)
            {
                level.LoadContent(Content);
            }

            hud.Reset();
            hud.MaxCarrot = level.CarrotCount;
            hud.MaxSalad = level.SaladCount;
            hud.MaxIdolGold = level.IdolCount;
            hud.MaxTime = level.MaxTime * 1000;
        }

        private void changeScreen(Screen screen)
        {
            currentScreen = screen;
            currentScreen.LoadContent(Content);
        }

        private void removeScreen()
        {
            currentScreen = null;
        }

        private void ExitGame()
        {
            this.Exit();
        }
    }
}
